package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.MarketController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.MarketRepository
import com.zxy.supplier_system.server.repository.SystemServiceRepository
import com.zxy.supplier_system.server.utils.IntIdAndName
import com.zxy.supplier_system.server.utils.orElse404
import com.zxy.supplier_system.server.utils.toIntIdAndName
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class MarketService(
    private val marketRepository: MarketRepository,
    private val systemServiceService: SystemServiceService,
    private val employeeService: EmployeeService,
    private val employeeOperateRecordService: EmployeeOperateRecordService,
) {


    @Transactional(readOnly = true)
    fun queryMarkets(
        pageable: Pageable,
        queryMarketsRequest: MarketController.QueryMarketsRequest
    ): Page<MarketController.QueryMarketsResponse> {
        val employee = employeeService.getRequiredCurrentEmployee()
        return this.marketRepository.findAll(
            MarketRepository.Specifications.fromQueryMarket(queryMarketsRequest)
                .and(
                    SystemServiceRepository.Specifications.systemServiceEquals(
                        { root -> root },
                        systemServiceService.getCurrentSystemService()
                    )
                ).and(
                    MarketRepository.Specifications.ifUCanSee(employee)
                ),
            pageable
        ).map { market ->
            MarketController.QueryMarketsResponse(
                market.name,
                market.id,
                market.saleManager?.let {
                    IntIdAndName(it.id!!, it.name)
                },
                market.excludeBrands.map { it.toIntIdAndName() },
                market.shoppingGuides.map { it.employee.toIntIdAndName() },
                market.goodsList.size,
                market.district?.parent?.parent?.toAdministrativeRegionResponse(),
                market.district?.parent?.toAdministrativeRegionResponse(),
                market.district?.toAdministrativeRegionResponse(),
                market.longitude,
                market.latitude,
                market.address,
                market.addressName
            )
        }
    }

    @Transactional
    fun updateMarket(id: Int, updateMarketRequest: MarketController.UpdateMarketRequest) {
        val market = this.marketRepository.findByIdOrNull(id).orElse404()
        market.name = updateMarketRequest.name
        market.saleManager = Employee().apply {
            this.id = updateMarketRequest.saleManagerId
        }
        market.excludeBrands = updateMarketRequest.excludeBrandIds.map {
            GoodsBrand().apply {
                this.id = it
            }
        }.toMutableList()
        market.shoppingGuides.clear();
        market.shoppingGuides.addAll(updateMarketRequest.shoppingGuideIds.map {
            MarketShoppingGuide().apply {
                this.employee = Employee().apply {
                    this.id = it
                }
                this.market = market
            }
        })
        market.district =updateMarketRequest.districtCode?.let {
            AdministrativeRegion().apply {
                this.code = updateMarketRequest.districtCode
            }
        }
        market.longitude = updateMarketRequest.longitude
        market.latitude = updateMarketRequest.latitude
        market.address = updateMarketRequest.address
        market.addressName = updateMarketRequest.addressName
        this.marketRepository.save(market)
        this.employeeOperateRecordService.updateMarket(market)
    }

    @Transactional
    fun createMarket(updateMarketRequest: MarketController.UpdateMarketRequest): Market {
        val market = this.marketRepository.save(Market().apply {
            name = updateMarketRequest.name
            if (updateMarketRequest.saleManagerId != null)
                saleManager = Employee().apply {
                    this.id = updateMarketRequest.saleManagerId
                }
            excludeBrands = updateMarketRequest.excludeBrandIds.map {
                GoodsBrand().apply {
                    this.id = it
                }
            }.toMutableList()
            systemService = systemServiceService.getCurrentSystemService()
            longitude = updateMarketRequest.longitude
            latitude = updateMarketRequest.latitude
            address = updateMarketRequest.address
            addressName = updateMarketRequest.addressName
            district =updateMarketRequest.districtCode?.let {
                AdministrativeRegion().apply {
                    this.code = updateMarketRequest.districtCode
                }
            }
        })
        if (updateMarketRequest.shoppingGuideIds.isNotEmpty()) {

            market.shoppingGuides.clear();
            market.shoppingGuides.addAll(updateMarketRequest.shoppingGuideIds.map {
                MarketShoppingGuide().apply {
                    this.employee = Employee().apply {
                        this.id = it
                    }
                    this.market = market
                }
            })

            marketRepository.save(market)
        }
        this.employeeOperateRecordService.createMarket(market)
        return market
    }

    @Transactional
    fun deleteMarket(id: Int) {
        this.marketRepository.deleteById(id)
    }


    fun getAllMarkets(): List<IntIdAndName> {
        return this.marketRepository.findAllBySystemService(systemServiceService.getCurrentSystemService()).map {
            IntIdAndName(it.id, it.name)
        }
    }

    @Transactional(readOnly = true)
    fun getMarketDetail(id: Int): MarketDetail {
        val employee = employeeService.getRequiredCurrentEmployee()
        return with(
            this.marketRepository.findBy<Market, Market?>(
                MarketRepository.Specifications.systemServiceEquals(employee.systemService).and(
                    MarketRepository.Specifications.idEqual(id).and(MarketRepository.Specifications.ifUCanSee(employee))
                )
            ) {
                it.oneValue()
            }
                .orElse404()
        ) {
            MarketDetail(
                id,
                name,
                saleManager?.toIntIdAndName(),
                this.shoppingGuides.map { it.employee.toIntIdAndName() },
                this.excludeBrands.map { it.toIntIdAndName() },
                this.goodsList.size,
                this.orders.size,
                this.district?.parent?.parent?.toAdministrativeRegionResponse(),
                this.district?.parent?.toAdministrativeRegionResponse(),
                this.district?.toAdministrativeRegionResponse(),
            )
        }
    }

    fun queryMarketSummary(keyword: String?, pageable: Pageable): Page<IntIdAndName> {
        return this.marketRepository.findAll(MarketRepository.Specifications.nameContains(keyword), pageable).map {
            it.toIntIdAndName()
        }
    }

    fun getAllMarketsOnlyICanSee(): List<IntIdAndName> {
        return this.marketRepository.findAll(MarketRepository.Specifications.ifUCanSee(employeeService.getRequiredCurrentEmployee()))
            .map {
                it.toIntIdAndName()
            }
    }

    data class MarketDetail(
        val id: Int,
        val name: String,
        val saleManager: IntIdAndName?,
        val shoppingGuides: List<IntIdAndName> = emptyList(),
        val excludeBrands: List<IntIdAndName> = emptyList(),
        val goodsAmount: Int = 0,
        val orderAmount: Int = 0,
        val province: AdministrativeRegionResponse?,
        val city: AdministrativeRegionResponse?,
        val district: AdministrativeRegionResponse?,
    ) {
        class AdministrativeRegionResponse(
            val code: String,
            val name: String,
        )
    }


}
